#
# MNIST
# MNIST是一个入门级的计算机视觉数据集，它包含各种手写数字图片及对应标签
#

from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
import tensorflow as tf
# 载入数据
mnist = read_data_sets("MNIST_data/", one_hot=True)

# 通过操作符号变量描述可交互单元
# x不是一个特定的值，而是一个占位符
# 我们在TensorFlow运行计算时输入这个值，我们希望能够输入任何数量的MNIST图像，每一幅图像展开成784维的向量
# 我们使用2维的浮点数张量来表示这些图，这个张量的形状是[None,784],这里None表示此张量的第一个维度可以使任何长度的
x = tf.placeholder(tf.float32,[None,784])

# 我们的模型也需要权重值和偏置量，当然我们可以把它们当做是另外的输入（使用占位符）
# 但TensorFlow有一个更好的方法表示它们：Variable
# 一个Variable表示一个可修改的张量，存在在TensorFlow的用于描述交互式操作的图中
# 它们可以用于计算输入值，也可以在计算中被修改
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

# 我们赋予tf.Variable不同的处值来创建不同的Variable，这里我们使用全为0的张量来初始化W和b
# W的维度是[784,10],因为我们想要用784维的图片向量乘以它得到一个10维的证据值向量，每一位对应不同的数字类
# b的形状是[10],所以我们可以直接把它加到输出上
y = tf.nn.softmax(tf.matmul(x,W)+b)

# 为了训练我们的模型，我们需要定义一个指标来评估这个模型的好坏
# 通常在机器学习中，这个指标称为成本（cost）或损失（loss),然后尽量最小化这个指标
# 一个非常见，优秀的成本函数时交叉熵（cross-entropy）。
# 交叉熵产生于信息论里面的信息压缩编码技术，定义如下
# H(y)=-[y1`*log(y1)+y2`*log(y2)+...+yn`*log(yn)]
# y是我们预测的概论分布，y`是实际的分布
# 简单理解就是，交叉熵是用来衡量我们的预测用于描述真相的低效性。
y_ = tf.placeholder("float",[None,10])

# 计算交叉熵
cross_entropy = -tf.reduce_sum(y_*tf.log(y))

# 首先，用tf.log计算y的每个元素对应的对数
# 接下来，我们把y_的每一个元素和tf.log(y)的对应元素相乘
# 最后，用tf.reduce_sum计算张量的所有元素的总和
# 注意这里的交叉熵不仅仅是用来衡量单一的一对预测和真实值，而是该批次训练的所有交叉熵的总和
#
# TensorFlow训练模型是非常容易的
# 因为TensorFlow拥有一张描述你各个计算单元的图，它可以自动的使用反向传播算法(backpropagation algorithm)
# 来有效地确定你的变量时如何影响你想要的最小化的那个成本值
# 然后Tensorflow会用你选择的优化算法来不断的修改的修改稿变量以降低成本
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
# 上面我们要求TensorFlow采用梯度下降算法（gradient descent algorithm)以0.01的学习速率最小化交叉熵

# 上面我们已经设置好我们的模型了，在运行计算前，我们需要添加一个操作来初始化我们创建的变量
init = tf.global_variables_initializer()

# 现在我们在一个Session中启动我们的模型，并初始化变量
sess = tf.Session()
sess.run(init)

# 然后开始训练模型，这里我们让模型循环训练10000次
for i in range(10000):
    batch_xs,batch_ys = mnist.train.next_batch(100)
    sess.run(train_step,feed_dict={x:batch_xs,y_:batch_ys})


#
# 评估我们的模型
# 首先我们找到那些正确预测的标签
# tf.argmax是一个非常有用的函数，它能给出某个tensor对象在某一维上其数据最大值所在的索引。
# 由于标签向量是由0,1组成，因此最大值1所在的索引位置就是类别标签
# 比如tf.argmax(y,1)返回的是模型对于任意输入x预测到的标签值
# 而tf.argmax(y_,1)代表正确的标签，我们可以用tf.equal来检测我们的预测是否与真实标签匹配（索引位置一样表示匹配）
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(y_,1))

# 上面代码会给我们一组布尔值
# 为了确定正确预测项的比例，我们可以把布尔值转换为浮点数，然后求平均值
# 如[True,Flase,True,True]会变成[1,0,1,1],取平均值后为0.75
accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))

# 最后，我们计算所学习到的模型在测试数据集上的正确率
print(sess.run(accuracy,feed_dict={x:mnist.test.images,y_:mnist.test.labels}))

